
package stcu;

import stcu.robjects.*;
import com.nvlled.route.*;
import java.util.concurrent.*;
import java.net.SocketAddress;

import java.util.*;

public class Group {

    private String groupId;
    protected ObjectRouter router;

    protected String idNumber;
    protected Set<String> whitelist;
    protected Set<String> pending;
    protected Map<String, MemberAdd> members;
    private boolean host; 

    private Set<GroupMessage> messageLog;
    private BlockingQueue<Listener> listeners;

    // TODO: Change groupId type to int
    protected Group(ObjectRouter router, String gid, Account account, boolean host) {
        this.idNumber = account.getIDNumber();
        this.groupId = gid;
        this.router = router;
        this.host = host;
        messageLog = new HashSet<GroupMessage>();
        whitelist = new HashSet<String>();
        pending = new HashSet<String>();
        members = new ConcurrentHashMap<String, MemberAdd>();
        listeners = new LinkedBlockingQueue<Listener>();

        whitelist.add(idNumber);
        members.put(account.getIDNumber(), 
                new MemberAdd(gid, account).setAddress(router.getLocalAddress()));
        host = true; // still deciding wether to drop this flag
    }

    public void addListener(Listener listener) {
        if (listener != null) {
            listeners.add(listener);
        }
    }

    public Set<String> getMembers() {
        Set<String> ids = new HashSet<String>();
        for (MemberAdd member: members.values()) {
            ids.add(member.getIDNumber());
        }
        return ids;
    }

    public String getId() {
        return groupId;
    }

    public void setFolderPath(String path) {
        // resend file list
    }

    public String getMemberId() {
        return idNumber;
    }

    public void leave() {
        // Still deciding what to do if the group creator leaves
        router.deliver(new GroupLeave(idNumber, groupId), groupId);
        // unregister from demux 
    }

    public void listMembers() {
        router.deliver(new MemberList(groupId), groupId);
    }

    public void send(String message) {
        router.deliver(new GroupSend(idNumber, groupId, message), groupId);
    }

    /*
    public void addMember(String name) {
        whitelist.add(name);
        if (pending.contains(name)) {
            pending.remove(name);
            router.deliver(new MemberAdd(groupId, name), getMembers());
            router.deliver(new GroupJoin.Status("ok", groupId), name);
            members.add(name);
        }
    }
    */

    public boolean isAllowed(String name) {
        return whitelist.contains(name);
    }

    public void receive(GroupLeave member) {
        if (host) {
            String id = member.getIDNumber();
            if (members.containsKey(id)) {
                Account account = members.remove(id).getAccount();
                router.deliver(new MemberDel(groupId, account), getMembers());
            }
        }
    }

    public void receive(GroupDel del) {
        //javax.swing.JOptionPane.showMessageDialog(null, "Group deleted " + del);
    }

    public void receive(MemberList message) {
        if (host) {
            for (MemberAdd member: members.values()) {
                router.deliver(member.setOld(), message.getSourceAddress());
            }
        }
    }

    public void receive(GroupMessage message) {
        // TODO: check if object is received from group creator
        messageLog.add(message);
        for (Listener l: listeners) {
            l.messageReceived(message);
        }
    }

    public Set<GroupMessage> getMessageLog() {
        return messageLog;
    }

    public void receive(GroupSend message) {
        if (host) {
            String id = message.getIDNumber();
            String name = members.get(id).getName();
            if (name != null && members.containsKey(id)) {
                router.deliver(new GroupMessage(name, groupId, message.getText()), getMembers());
            } else {
                // log
            }
        }
    }

    public void receive(MemberAdd member) {
        // TODO: check if object is received from group creator
        AddressTable table = router.getTable();
        String id = member.getIDNumber();

        table.store(id, member.getAddress());
        members.put(id, member);

        for (Listener l: listeners) {
            l.memberJoined(member);
        }
    }
    public void receive(MemberDel member) {
        members.remove(member.getIDNumber());
        for (Listener l: listeners) {
            l.memberLeft(member);
        }
    }

    public void receive(GroupJoin request) {
        if (host) {
            Account account = request.getAccount();
            long id = request.getId();
            if (true) {
                SocketAddress address = request.getSourceAddress();
                MemberAdd newMember = 
                    new MemberAdd(groupId, account).setAddress(address);

                router.deliver(new GroupJoin.Status(id, "ok", groupId), address);
                router.deliver(newMember, getMembers());
                members.put(account.getIDNumber(), newMember);
                AddressTable table = router.getTable();
                table.store(account.getIDNumber(), request.getSourceAddress());
            } else {
                pending.add(account.getIDNumber());
            }
        }
    }

    public static interface Listener {
        public void messageReceived(GroupMessage message);

        public void memberJoined(MemberAdd member);

        public void memberLeft(MemberDel member);
    }

    // Null pointers are evil. Use this as an initial value for the listener.
    private class Deaf implements Listener {
        public void messageReceived(GroupMessage message){ }

        public void memberJoined(MemberAdd member){ }

        public void memberLeft(MemberDel member){ }
    }
}


